//
// Buffer.h
// libprotea
// Tools Library for the Protea Project / Buffer functionality
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
//  Buffer definitions
//
#ifndef __PROTEA_BUFFER_H_
#define __PROTEA_BUFFER_H_

/**
	\file Buffer.h
	\brief Buffer class definition

	Contains all the functionality for a buffer.  Use the XCAppInterface object to
	create buffers.  Buffers contain a number of field objects.
*/

#include "../Appdic/appdic.h"
#include <stdlib.h>


class XCAppInterface;
class XCField;
class XCBuffer;
class XCBufferList;
class XCTransactionInfo;
class XCAppErrorHandler;

#define BUFFER_ADDREF( buffer ) BufferAdd( buffer )
#define BUFFER_RELEASE( buffer ) BufferRemove( buffer )

void BufferAdd( XCBuffer* );
__EXPORT void BufferRemove( XCBufferList* );
__EXPORT void BufferRemove( XCBuffer* );
void BufferDump( XCAppInterface* app, XCTransactionInfo* info );
void BufferClear();

/////////////////////////////////////////////////////////////////////////////
// XCBuffer

/**
	\brief Protea Buffer Class
	
	Contains all functionality for a buffer
*/
class __EXPORT XCBuffer : public XCBase {
public:
	/**
		\brief Constructor
	*/
	XCBuffer();

	virtual ~XCBuffer();

	/**
		\brief Returns a copy of the current buffer, including data

		The copy is a clone from the original, in a new dataspace, and needs to be released.

		Return values:
			New buffer
	*/
	XCBuffer* Copy() const;

	/**
		\brief Compares two field values
		\param Field1
		\param Field2

		Return values:
			Comparison result.  Will be either -1, 0, or 1
	*/
	long CompareFields( const long Field1, const long Field2 ) const;

	/**
		\brief Add fields together
		\param Field1	Source Field 1	
		\param Field2	Source Field 2
		\param Result	Result Field

		Return values:
			None

		Remarks:
			Adds Source fields 1 and 2 together and stores the result in the result field
			(result = f1 + f2)

			The result field may be the same as either of the source fields.

		See Also:
	*/
	void AddFields( const long Field1, const long Field2, const long Result );

	/**
		\brief Adds all corresponding fields
		\param source Source buffer
	*/
	void AddCorresponding( const XCBuffer* source );

	/**
		\brief Adds all corresponding fields that match an exchange buffer
		\param source Source buffer
		\param exchange Exchange buffer
		\param err Error Handler
	*/
	void AddCorrespondingExchange( const XCBuffer* source, XCBuffer* exchange, XCAppErrorHandler* err );

	/**
		\brief Subtract fields
		\param Field1	Source Field 1	
		\param Field2	Source Field 2
		\param Result	Result Field

		Substract source field 1 from field 2 and stores the result in the result field
		(result = f1 - f2)

		The result field may be the same as either of the source fields.
	*/
	void SubFields( const long Field1, const long Field2, const long Result );

	/**
		\brief Subtracts all corresponding fields
		\param source Source buffer
	*/
	void SubCorresponding( XCBuffer* source );

	/**
		\brief Subtracts all corresponding fields that match an exchange buffer
		\param source Source buffer
		\param exchange Exchange buffer
		\param err Error Handler
	*/
	void SubCorrespondingExchange( XCBuffer* source, XCBuffer* exchange, XCAppErrorHandler* err );

	/**
		\brief Multiply fields
		\param Field1	Source Field 1	
		\param Field2	Source Field 2
		\param Result	Result Field

		Multiplies source field 1 by field 2 and stores the result in the result field
		(result = f1 * f2)

		The result field may be the same as either of the source fields.
	*/
	void MultFields( const long Field1, const long Field2, const long Result );

	/**
		\brief Divide fields
		\param Field1	Source Field 1	
		\param Field2	Source Field 2
		\param Result	Result Field

		Divides source field 1 by field 2 and stores the result in the result field
		(result = f1 / f2)

		The result field may be the same as either of the source fields.
	*/
	void DivFields( const long Field1, const long Field2, const long Result );

	/**
		\brief Add a constant value to a field
		\param Field Result field
		\param value Constant value
	*/
	int AddConst( const long Field, const long value );

	/**
		\brief Subtract a constant value from a field
		\param Field Result field
		\param value Constant value
	*/
	int SubConst( const long Field, const long value );

	/**
		\brief Multiply a field by a constant value
		\param Field Result field
		\param value Constant value
	*/
	int MultConst( const long Field, const long value );

	/**
		\brief Divide a field by a constant value
		\param Field Result field
		\param value Constant value
	*/
	int DivConst( const long Field, const long value );
	
	/**
		\brief Set all fields to blank

		All string/date/time fields are set to NULL, numeric fields are set to 0
	*/
	void Clear();

	/**
		\brief Tests whether every field in a buffer is blank

		Returns true if all fields in the buffer are 'blank', otherwise, returns false
	*/
	bool IsBlank();

	/**
		\brief Tests whether a buffer is blank, excluding certain fields
		\param count Number of supplied fields
		\param ... Supplied field numbers

		Returns true if all fields in the buffer except the specified fields are blank,
		otherwise, returns false
	*/
	bool IsBlank( int count, ... );

	/** 
		\brief Returns true if anything in the buffer has changed
	*/
	bool IsChanged();

	/**
		\brief Returns true if the given field has changed
		\param field_number Field number to compare
		\param compare Buffer to compare against
	*/
	bool IsChanged( long field_number, XCBuffer* compare );

	/**
		\brief Resets the field change flags
		\param defaultval Flag state, true or false
	*/
	void ResetChanges( bool defaultval = true );

	/**
		\brief Compares two buffers and sets change flags in this buffer
		\param compare Buffer to compare against

		Returns true if any field in the buffer has changed, otherwise, returns false.
	*/
	bool MarkChanges( const XCBuffer* compare );

	/**
		\brief Fill the buffer with default values
	*/
	void LoadDefaults();


	/**
		\brief Fills blank fields in the buffer with defaults

		Similar to LoadDefaults, but will only load the default value if the field is blank.
	*/
	void FillDefaults();


	/**
		\brief Copy field value from source buffer to this buffer
		\param source Source buffer
		\param FieldNumber Source Field Number
		\param MoveBlanks Determines if the value should be moved over if the source field is blank

		<b>Remarks:</b>
			<br>The supplied field must have a name that matches a field in the buffer.
			
			To move field data between fields with different names, use the MoveUnlikeField function

		<b>See Also:</b>
			<br>XCBuffer::MoveUnlikeField, XCBuffer::MoveCorresponding
	*/
	void MoveField( const XCBuffer* source, const long FieldNumber, const bool MoveBlanks=false );


	/**
		\brief Copy given field value from source buffer to target field in this buffer

		\param tgt_fld Target field number (must exist in this buffer)
		\param source Source buffer
		\param src_fld Source field number
		\param MoveBlanks Determines if the value should be moved over if the source field is blank

		<b>Remarks:</b>
			<br>If the source field can not be found in the source buffer, or the target
			field can not be located in the current buffer, an error is raised

		<b>See Also:</b>
			<br>XCBuffer::MoveField, XCBuffer::MoveCorresponding
	*/
	void MoveUnlikeField( const long tgt_fld, const XCBuffer *source, const long src_fld, const bool MoveBlanks = false );


	/** 
		\brief Move all fields from source with like names
		\param source Source buffer
		\param ErrorList Error collection object (defaults to NULL)
		\param MoveBlanks Determines if the value should be moved over if the source field is blank

		<b>Return values:</b>
			<br>NONE

		<b>Remarks:</b>
			<br>All fields with matching names will be moved from the source buffer
			to the current buffer.

			If fields need to be moved that have different field names, a
			call will have to be made to MoveUnlikeField.

			Automatically does type conversions between buffers.  In the case of a blank
			value being converted to a numeric or literal type, the default for the field
			is used.

		<b>See Also:</b>
			<br>XCBuffer::MoveUnlikeField, XCBuffer::MoveField
	*/
	void MoveCorresponding( const XCBuffer* source, XCAppErrorHandler* ErrorList, const bool MoveBlanks=false );

	/**
		\brief Move corresponding, using an exchange buffer
		\param source Source buffer
		\param exchange Exchange buffer
		\param ErrorList Error handler
	*/
	void MoveCorrespondingExchange( const XCBuffer* source, XCBuffer* exchange, XCAppErrorHandler* ErrorList = NULL );


		
	
	/**
		\brief Force validation of all fields in the buffer

		Deprecated, don't use.
	*/
	void Validate();

	/**
		\brief Return the format name
	*/
	char* GetFormatName();

	/**
		\brief Returns a pointer to the buffer's data area
	*/
	void* GetData()	const;

	/**
		\brief Returns the size of the buffer's data area

		This is the total size of the buffer, excluding blob data
	*/
	long GetSize() const;

	/**
		\brief Returns the size of the buffer's data area plus that of any BLOBs

		This is the total size of the buffer
	*/
	long GetSizeWithBLOBs() const;

	/**
		\brief Returns the format number of the buffer
	*/
	long GetFormatNumber() const;

	/**
		\brief Returns the child format number
	*/
	long GetChildFormat() const;

	/**
		\brief Return the number of fields in the buffer
	*/
	long GetFieldCount() const;

	/**
		\brief Find a field based on field number
		\param FieldNumber Field number to return

		If the field is found in the buffer, it will return the pointer to the XCField object,
		otherwise, it will return NULL
	*/
	XCField* GetField( const long FieldNumber ) const;

	/**
		\brief Find a field based on its index
		\param index Index of the field to return
	*/
	XCField* GetFieldByIndex( const long index ) const;

	/**
		\brief Returns the format type
	*/
	int GetType() const { return mydesc->type; };

	/**
		\brief Debug print of the buffer

		<b>Remarks:</b>
			<br>Actual debug data output depends on set tracing levels

		<b>See Also:</b>
			<br>XCAppInterface::ToolsTrace
	*/
	void DebugDump() const;

	/**
		\brief Set the read-only flag for the buffer
		\param mode True if buffer is read-only

		When a buffer is read-only, no commands that would alter the buffer may be executed
		against that buffer
	*/
	void SetReadOnly( bool mode );

	/**
		\brief Returns wether or not the buffer contains a BLOB
	*/
	bool ContainsBLOB();	

/****
	INTERNAL FUNCTIONS/VARIABLES

	Do not document!!!
****/

	// Check the minimum size of entered fields
	void CheckFieldLengths( XCAppErrorHandler* errors );

	// Create a dynamic buffer given a list of field numbers
	int CreateDynamic( const long* FieldNumbers, bool Load_Defaults, long FormatNumber, XCAppInterface* new_owner);

	// Create a dynamic buffer given a list of data structure records
	int CreateDynamic( int count, const buffer_ds* Fields, long FormatNumber, XCAppInterface* new_owner );

	// Create a dynamic buffer given a buffer format record
	int CreateDynamic( buffer_format* fmt, XCAppInterface* new_owner );

	// Create a buffer using the given data buffer
	int CreateFromData( long FormatNumber, void* data, XCAppInterface* new_owner );

	// Create a buffer
	int Create( long FormatNumber, bool load_defaults, XCAppInterface* new_owner);

	// Set wether or not we need to deal with a BLOB in the buffer
	void SetContainsBLOB( bool cBLOB );

	// Obtain how big a message will be (with BLOBs)
	int GetMsgSize();

	// Put into a binary buffer, including BLOBs
	void PutIntoMessage( unsigned char *buffer, int maxsize );

	buffer_db_defn* GetDatabaseInfo();
	XCAppInterface* APPDIC;
	buffer_format*	mydesc;
	buffer_db_tables* GetDatasetDescr();

protected:
	int PrepareBuffer( int load_mode );

	typedef struct field_index_t {
		int record_num;
		int fld_nbr;
	};

	char* data;
	XCField* FieldList;
	field_index_t* field_index;

	bool IsDynamic;
	bool IsDynamicFields;
	bool HasBLOB;
};




/*
//
// Stream I/O commands for buffers
//
ostream& operator<<( ostream& s, XCBuffer& str );
ostream& operator>>( ostream& s, XCBuffer& str );
*/

#endif
